import sys
import matplotlib
import pandas as pd
import random
import json
import os
import matplotlib.pyplot as plt
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
from PyQt5.QtWidgets import (
    QApplication, QWidget, QVBoxLayout, QHBoxLayout,
    QPushButton, QTableWidget, QTableWidgetItem,
    QMessageBox, QFileDialog, QStackedWidget, QLabel, QFrame, QSpacerItem, QSizePolicy, QComboBox, QDialog
)

matplotlib.rcParams['font.family'] = 'sans-serif'
matplotlib.rcParams['font.sans-serif'] = ['SimHei']
DATA_FILE = 'students_data.json'

class AttendanceApp(QWidget):


    def __init__(self):
        super().__init__()
        self.students = {}
        self.sidebar_visible = True
        self.class_combo = QComboBox()
        self.class_combo_rate = QComboBox()
        self.initUI()
        self.load_students()

    '''初始化界面'''
    def initUI(self):
        self.setWindowTitle('课堂点名系统')
        self.setGeometry(100, 100, 800, 600)
        self.mainLayout = QHBoxLayout(self)
        self.sidebarLayout = QVBoxLayout()
        self.sidebarFrame = QFrame()
        self.sidebarFrame.setLayout(self.sidebarLayout)

        self.btnToggleSidebar = QPushButton('<<<')
        self.btnStudentList = QPushButton('学生名单')
        self.btnAttendance = QPushButton('点名')
        self.btnAttendanceRate = QPushButton('班级出勤率')
        self.btnGenerateReport = QPushButton('生成出勤报告')

        self.btnToggleSidebar.clicked.connect(self.toggleSidebar)
        self.btnStudentList.clicked.connect(self.toggleStudentList)
        self.btnAttendance.clicked.connect(self.toggleAttendance)
        self.btnAttendanceRate.clicked.connect(self.toggleAttendanceRate)
        self.btnGenerateReport.clicked.connect(self.generateAttendanceReport)

        self.sidebarLayout.addWidget(self.btnToggleSidebar)
        self.sidebarLayout.addWidget(self.btnStudentList)
        self.sidebarLayout.addWidget(self.btnAttendance)
        self.sidebarLayout.addWidget(self.btnAttendanceRate)
        self.sidebarLayout.addWidget(self.btnGenerateReport)

        self.spacer = QSpacerItem(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)
        self.sidebarLayout.addItem(self.spacer)

        self.stackedWidget = QStackedWidget()
        self.studentListPage()
        self.attendancePage()
        self.attendanceRatePage()

        self.mainLayout.addWidget(self.sidebarFrame)
        self.mainLayout.addWidget(self.stackedWidget)
        self.setLayout(self.mainLayout)
        self.stackedWidget.setCurrentIndex(0)

    '''创建学生名单页面'''
    def studentListPage(self):
        self.studentTable = QTableWidget()
        self.studentTable.setColumnCount(4)
        self.studentTable.setHorizontalHeaderLabels(["班级", "姓名", "学号", "出勤状态"])
        self.class_combo.currentIndexChanged.connect(self.filterStudentsByClass)
        self.btnImport = QPushButton('导入学生名单')
        self.btnImport.clicked.connect(self.importStudents)

        studentLayout = QVBoxLayout()
        studentLayout.addWidget(QLabel("选择班级："))
        studentLayout.addWidget(self.class_combo)
        studentLayout.addWidget(self.studentTable)
        studentLayout.addWidget(self.btnImport)

        studentPage = QWidget()
        studentPage.setLayout(studentLayout)
        self.stackedWidget.addWidget(studentPage)
        self.populateStudentTable()

    '''填充学生信息到表格中'''
    def populateStudentTable(self):
        self.studentTable.setRowCount(0)
        for student_id, (student_class, student_name, attendance_status) in self.students.items():
            row_position = self.studentTable.rowCount()
            self.studentTable.insertRow(row_position)
            self.studentTable.setItem(row_position, 0, QTableWidgetItem(student_class))
            self.studentTable.setItem(row_position, 1, QTableWidgetItem(student_name))
            self.studentTable.setItem(row_position, 2, QTableWidgetItem(str(student_id)))

            attendance_combo = QComboBox()
            attendance_combo.addItems(['出勤', '请假', '旷课'])
            attendance_combo.setCurrentText(attendance_status)
            attendance_combo.currentIndexChanged.connect(lambda index, student_id=student_id: self.updateAttendanceStatusFromComboBox(student_id, attendance_combo.currentText()))
            self.studentTable.setCellWidget(row_position, 3, attendance_combo)

    '''根据下拉菜单更新选定学生的出勤状态'''
    def updateAttendanceStatusFromComboBox(self, student_id, status):
        if student_id in self.students:
            student_class, student_name, _ = self.students[student_id]
            self.students[student_id] = (student_class, student_name, status)
            for row in range(self.studentTable.rowCount()):
                if self.studentTable.item(row, 2).text() == str(student_id):
                    attendance_combo = self.studentTable.cellWidget(row, 3)
                    attendance_combo.setCurrentText(status)
            QMessageBox.information(self, '成功', f'{student_name} 的出勤状态已更新为 "{status}"！')

    '''创建点名页面'''
    def attendancePage(self):
        attendancePage = QWidget()
        attendanceLayout = QVBoxLayout()
        attendanceHeader = QLabel("随机抽取学生：")
        attendanceHeader.setStyleSheet("font-size: 20px; color: black;")
        self.current_student_label = QLabel("")
        self.current_student_label.setStyleSheet("font-size: 36px; font-weight: bold; color: black; background-color: lightgray; padding: 10px;")
        self.btnRandom = QPushButton('随机抽取学生')
        self.btnRandom.clicked.connect(self.randomStudent)

        self.attendance_buttons_layout = QVBoxLayout()
        self.btnAttend = QPushButton('出勤')
        self.btnLeave = QPushButton('请假')
        self.btnAbsent = QPushButton('旷课')

        self.btnAttend.clicked.connect(lambda: self.updateAttendanceStatus("出勤"))
        self.btnLeave.clicked.connect(lambda: self.updateAttendanceStatus("请假"))
        self.btnAbsent.clicked.connect(lambda: self.updateAttendanceStatus("旷课"))

        self.attendance_buttons_layout.addWidget(self.btnAttend)
        self.attendance_buttons_layout.addWidget(self.btnLeave)
        self.attendance_buttons_layout.addWidget(self.btnAbsent)

        attendanceLayout.addWidget(attendanceHeader)
        attendanceLayout.addWidget(self.current_student_label)
        attendanceLayout.addWidget(self.btnRandom)
        attendanceLayout.addLayout(self.attendance_buttons_layout)

        attendancePage.setLayout(attendanceLayout)
        self.stackedWidget.addWidget(attendancePage)

    '''创建计算出勤率界面'''
    def attendanceRatePage(self):
        attendanceRatePage = QWidget()
        attendanceRateLayout = QVBoxLayout()
        self.attendanceRateLabel = QLabel("选择班级以查看出勤率：")
        self.class_combo_rate = QComboBox()
        self.class_combo_rate.currentIndexChanged.connect(self.plotAttendanceRate)

        attendanceRateLayout.addWidget(self.attendanceRateLabel)
        attendanceRateLayout.addWidget(self.class_combo_rate)
        self.btnShowRate = QPushButton('显示出勤率')
        self.btnShowRate.clicked.connect(self.plotAttendanceRate)
        attendanceRateLayout.addWidget(self.btnShowRate)

        self.figure = plt.figure()
        self.canvas = FigureCanvas(self.figure)
        attendanceRateLayout.addWidget(self.canvas)

        attendanceRatePage.setLayout(attendanceRateLayout)
        self.stackedWidget.addWidget(attendanceRatePage)

    '''根据选择的班级筛选学生'''
    def filterStudentsByClass(self):
        selected_class = self.class_combo.currentText()
        self.studentTable.setRowCount(0)
        for student_id, (student_class, student_name, attendance_status) in self.students.items():
            if student_class == selected_class or selected_class == "所有班级":
                row_position = self.studentTable.rowCount()
                self.studentTable.insertRow(row_position)
                self.studentTable.setItem(row_position, 0, QTableWidgetItem(student_class))
                self.studentTable.setItem(row_position, 1, QTableWidgetItem(student_name))
                self.studentTable.setItem(row_position, 2, QTableWidgetItem(str(student_id)))

                attendance_combo = QComboBox()
                attendance_combo.addItems(['出勤', '请假', '旷课'])
                attendance_combo.setCurrentText(attendance_status)
                attendance_combo.currentIndexChanged.connect(lambda index, student_id=student_id: self.updateAttendanceStatusFromComboBox(student_id, attendance_combo.currentText()))
                self.studentTable.setCellWidget(row_position, 3, attendance_combo)

    '''随机选择一个学生点名'''
    def randomStudent(self):
        if not self.students:
            QMessageBox.warning(self, '警告', '请先导入学生名单！')
            return
        selected_class = self.class_combo.currentText()
        eligible_students = [
            student_id for student_id, (student_class, student_name, _) in self.students.items()
            if student_class == selected_class or selected_class == "所有班级"
        ]

        if not eligible_students:
            QMessageBox.warning(self, '警告', '当前班级没有学生，请确认导入的名单！')
            return
        student_id = random.choice(eligible_students)
        student_class, student_name, _ = self.students[student_id]

        if student_name:
            self.current_student_label.setText(student_name)
        else:
            QMessageBox.warning(self, '警告', '随机选择的学生没有姓名！')

    '''更新抽到的学生的出勤状态'''
    def updateAttendanceStatus(self, status):
        student_name = self.current_student_label.text()
        if student_name == "":
            QMessageBox.warning(self, '警告', '请先随机抽取学生！')
            return
        for student_id, (student_class, name, _) in self.students.items():
            if name == student_name:
                self.students[student_id] = (student_class, name, status)
                for row in range(self.studentTable.rowCount()):
                    if self.studentTable.item(row, 1).text() == name:
                        self.studentTable.setItem(row, 3, QTableWidgetItem(status))
                QMessageBox.information(self, '成功', f'{student_name} 的出勤状态已更新为 "{status}"！')
                break

    '''切换侧边栏的显示状态'''
    def toggleSidebar(self):
        if self.sidebar_visible:
            self.sidebarFrame.setMinimumWidth(40)
            self.spacer.changeSize(20, 20, QSizePolicy.Minimum, QSizePolicy.Expanding)
        else:
            self.sidebarFrame.setMinimumWidth(200)
            self.spacer.changeSize(20, 20, QSizePolicy.Minimum, QSizePolicy.Minimum)
        self.sidebar_visible = not self.sidebar_visible

    '''切换至学生名单界面'''
    def toggleStudentList(self):
        self.toggleSection(0)

    '''切换至点名界面'''
    def toggleAttendance(self):
        self.toggleSection(1)

    '''切换至出勤率计算界面'''
    def toggleAttendanceRate(self):
        self.toggleSection(2)

    '''切换堆叠控件的当前显示页面'''
    def toggleSection(self, index):
        self.stackedWidget.setCurrentIndex(index)

    '''导入学生名单'''
    def importStudents(self):
        fileName, _ = QFileDialog.getOpenFileName(self, '选择文件', '', 'CSV Files (*.csv)')
        if fileName:
            try:
                data = pd.read_csv(fileName)
                for col in ['class', 'name', 'id']:
                    if col not in data.columns:
                        QMessageBox.warning(self, '警告', f'CSV 文件中未找到“{col}”列！')
                        return
                self.studentTable.clear()
                self.studentTable.setRowCount(0)
                self.studentTable.setHorizontalHeaderLabels(["班级", "姓名", "学号", "出勤状态"])
                self.students.clear()
                self.class_combo.clear()
                self.class_combo_rate.clear()
                unique_classes = {"所有班级"}

                for index, row in data.iterrows():
                    student_class = row['class']
                    student_name = row['name']
                    student_id = row['id']
                    attendance_status = '出勤' #默认的出勤状态
                    row_position = self.studentTable.rowCount()
                    self.studentTable.insertRow(row_position)
                    self.studentTable.setItem(row_position, 0, QTableWidgetItem(student_class))
                    self.studentTable.setItem(row_position, 1, QTableWidgetItem(student_name))
                    self.studentTable.setItem(row_position, 2, QTableWidgetItem(str(student_id)))
                    self.studentTable.setItem(row_position, 3, QTableWidgetItem(attendance_status))
                    self.students[student_id] = (student_class, student_name, attendance_status)
                    unique_classes.add(student_class)

                self.class_combo.addItems(unique_classes)
                self.class_combo_rate.addItems(unique_classes)
            except Exception as e:
                QMessageBox.critical(self, '错误', f'导入文件时出错: {str(e)}')

    '''如果不是第一次打开软件，通过第一次关闭时自动生成的json文件加载学生数据'''
    def load_students(self):
        if os.path.exists(DATA_FILE):
            with open(DATA_FILE, 'r', encoding='utf-8') as f:
                self.students = json.load(f)
                self.studentTable.setRowCount(0)
                unique_classes = {"所有班级"}
                for student_id, (student_class, student_name, attendance_status) in self.students.items():
                    row_position = self.studentTable.rowCount()
                    self.studentTable.insertRow(row_position)
                    self.studentTable.setItem(row_position, 0, QTableWidgetItem(student_class))
                    self.studentTable.setItem(row_position, 1, QTableWidgetItem(student_name))
                    self.studentTable.setItem(row_position, 2, QTableWidgetItem(str(student_id)))
                    self.studentTable.setItem(row_position, 3, QTableWidgetItem(attendance_status))
                    unique_classes.add(student_class)

                self.class_combo.addItems(unique_classes)
                self.class_combo_rate.addItems(unique_classes)

    '''保存学生数据'''
    def save_students(self):
        with open(DATA_FILE, 'w', encoding='utf-8') as f:
            json.dump(self.students, f, ensure_ascii=False, indent=4)

    '''关闭应用时触发保存学生数据的函数'''
    def closeEvent(self, event):
        self.save_students()
        event.accept()

    '''绘图呈现班级出勤率'''
    def plotAttendanceRate(self):
        selected_class = self.class_combo_rate.currentText()
        attendance_counts = {'出勤': 0, '请假': 0, '旷课': 0}
        for student_id, (student_class, _, attendance_status) in self.students.items():
            if student_class == selected_class or selected_class == "所有班级":
                if attendance_status in attendance_counts:
                    attendance_counts[attendance_status] += 1
                    sizes = list(attendance_counts.values())
        labels = attendance_counts.keys()
        if all(size == 0 for size in sizes):
            QMessageBox.warning(self, '警告', f'当前班级 {selected_class} 没有出勤数据！')
            return
        self.figure.clf()
        ax = self.figure.add_subplot(111)
        colors = ['#4CAF50', '#FFC107', '#F44336']
        explode = (0.1, 0, 0)
        ax.pie(sizes, explode=explode, labels=labels, colors=colors,
               autopct='%1.1f%%', shadow=True, startangle=140)
        ax.axis('equal')
        ax.set_title(f"{selected_class} 班级出勤率")
        self.canvas.draw()

    '''生成班级出勤率情况报告'''
    def generateAttendanceReport(self):
        selected_class = self.class_combo.currentText()
        attendance_counts = {'出勤': [], '请假': [], '旷课': []}
        for student_id, (student_class, student_name, attendance_status) in self.students.items():
            if student_class == selected_class or selected_class == "所有班级":
                if attendance_status == '出勤':
                    attendance_counts['出勤'].append(student_name)
                elif attendance_status == '请假':
                    attendance_counts['请假'].append(student_name)
                elif attendance_status == '旷课':
                    attendance_counts['旷课'].append(student_name)
        self.displayAttendanceReport(selected_class, attendance_counts)

    '''创建出勤率报告界面'''
    def displayAttendanceReport(self, selected_class, attendance_counts):
        report_dialog = QDialog(self)
        report_dialog.setWindowTitle(f"{selected_class} 班级出勤报告")
        report_dialog.setGeometry(100, 100, 400, 300)
        layout = QVBoxLayout()
        report_text = f"{selected_class} 班级出勤报告\n\n"
        report_text += "出勤名单:\n" + "\n".join(attendance_counts['出勤']) + "\n\n"
        report_text += "请假名单:\n" + "\n".join(attendance_counts['请假']) + "\n\n"
        report_text += "旷课名单:\n" + "\n".join(attendance_counts['旷课']) + "\n"
        report_label = QLabel(report_text)
        report_label.setWordWrap(True)
        layout.addWidget(report_label)
        close_button = QPushButton("关闭")
        close_button.clicked.connect(report_dialog.close)
        layout.addWidget(close_button)
        report_dialog.setLayout(layout)
        report_dialog.exec_()

if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = AttendanceApp()
    ex.show()
    sys.exit(app.exec_())
